﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using System.Collections;
using System.Linq.Expressions;
using Gate.ExpressionBuilder.DataStructure;
using Gate.ExpressionBuilder.Enums;
using System.Linq;

namespace Gate.ExpressionBuilder
{
    /// <summary>
    /// 表达式生成器
    /// </summary>
    public partial class ExpressionBuilder
    {
        /// <summary>
        /// 信号锁
        /// </summary>
        private static Semaphore LockSP = new Semaphore(1, 1);
        /// <summary>
        /// 用于缓存项目中的模型的字段类型
        /// </summary>
        private static Dictionary<string, Type> ModelColumnType = new Dictionary<string, Type>();

        #region 数据类型处理
        /// <summary>
        /// 将对对应数据转换成对应的类型的数据
        /// </summary>
        /// <param name="value">数据值</param>
        /// <param name="conversion">要转换的数据类型</param>
        /// <returns>转换后的数据</returns>
        private object ChangeType(object value, Type conversion)
        {
            if (value != null && typeof(IList).IsAssignableFrom(value.GetType()))
            {
                var listType = typeof(List<>).MakeGenericType(conversion);
                var list = Activator.CreateInstance(listType);
                var addMethod = listType.GetMethod("Add");
                foreach (var item in (IList)value)
                {
                    addMethod.Invoke(list, new object[] { ChangeType(item, conversion) });
                }
                return list;
            }
            else
            {
                if (conversion.IsGenericType && conversion.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
                {
                    if (value == null)
                    {
                        return null;
                    }
                    conversion = Nullable.GetUnderlyingType(conversion);
                }
                if (conversion.Name == "Guid" && value.GetType().Name == "String")
                {
                    return new Guid(value.ToString());
                }
                else
                {
                    if (conversion.IsEnum)
                    {
                        value = Convert.ChangeType(value, Enum.GetUnderlyingType(conversion));
                        return Enum.Parse(conversion, Enum.GetName(conversion, value));
                    }
                    else
                    {
                        return Convert.ChangeType(value, conversion);
                    }
                }
            }
        }
        /// <summary>
        /// 获得泛型的对应属性名称的属性类型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="columnName"></param>
        /// <returns></returns>
        private Type GetModelColumnType<T>(string columnName)
        {
            return GetModelColumnType(typeof(T), columnName);
        }
        /// <summary>
        /// 尝试在缓存中获得模型的，对应字段的数据类型
        /// </summary>
        /// <param name="type">类型</param>
        /// <param name="columnName">字段名称</param>
        /// <returns>获得字段的模型</returns>
        private Type GetModelColumnType(Type type, string columnName)
        {
            //ModelColumnType
            string key = string.Format("{0}.{1}", type.FullName, columnName);
            if (!ModelColumnType.ContainsKey(key))//如果存在缓存，则读取缓存
            {
                LockSP.WaitOne();//避免多线程争用
                try
                {
                    lock (ModelColumnType)
                    {
                        var propertys = type.GetProperties();
                        foreach (var item in propertys)
                        {
                            string columnKey = string.Format("{0}.{1}", type.FullName, item.Name);
                            if (!ModelColumnType.ContainsKey(columnKey))
                            {
                                ModelColumnType.Add(columnKey, item.PropertyType);
                            }

                        }
                    }
                    LockSP.Release();
                }
                catch (Exception)
                {
                    LockSP.Release();
                    throw;
                }
            }
            if (ModelColumnType.ContainsKey(key))
            {
                return ModelColumnType[key];
            }
            else
            {
                return null;
            }
        }
        #endregion
        /// <summary>
        /// 返回一个字段，用于OrderBy或者统计之类的场景
        /// 类似于o=>o.columnName
        /// </summary>
        /// <typeparam name="T1">目标类型</typeparam>
        /// <typeparam name="T2">对应字段类型</typeparam>
        /// <param name="name">字段名称</param>
        /// <returns></returns>
        public Expression<Func<T1, T2>> GetExpression<T1,T2>(string columnName)
        {
            Type tType = typeof(T1);
            ParameterExpression parameterExpression = Expression.Parameter(tType, "o");
            PropertyInfo propertyInfo = tType.GetProperty(columnName);
            Expression expression = Expression.Property(parameterExpression, propertyInfo);
            Expression<Func<T1, T2>> lambda = Expression.Lambda<Func<T1, T2>>(expression, parameterExpression);
            return lambda;
        }
        /// 转换成表达式树
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="queryWheres">查询条件，List中的item与item之间会以括号隔开，item内部的则先 且再或运算</param>
        /// <returns>Where的表达式树</returns>
        public Expression<Func<T, bool>> GetExpression<T>(List<WhereCondition> queryWheres)
        {
            //最终生成的表达式树
            Expression<Func<T, bool>> expression = null;
            if (queryWheres != null && queryWheres.Count != 0)
            {
                //先且再或
                queryWheres = queryWheres.OrderBy(o => o.LogicalType).ToList();
                //表达式本体
                Expression predicateBody = null;
                Type tType = typeof(T);
                ParameterExpression parameterExpression = Expression.Parameter(tType, "o");
                //先处理WHERE
                foreach (var queryWhere in queryWheres)
                {
                    Expression predicateBodyChild = null;
                    //条件节点没有数据则跳过
                    if (queryWhere == null || queryWhere.WhereItems == null || queryWhere.WhereItems.Count == 0)
                        continue;
                    queryWhere.WhereItems = queryWhere.WhereItems.OrderBy(o => o.LogicalType).ToList();
                    foreach (var item in queryWhere.WhereItems)
                    {
                        //不需要判断的话，就跳过
                        if (item.LogicalType == Logical.Pass)
                            continue;
                        //获得表达式节点
                        //左值表达式
                        Expression lExpression;//= Expression.Property(parameterExpression, item.ColumnName);
                        if (string.IsNullOrWhiteSpace(item.LColumnName))
                        {
                            throw new ArgumentNullException(nameof(item.LColumnName));
                        }
                        else
                        {
                            PropertyInfo propertyInfo = tType.GetProperty(item.LColumnName);
                            if (propertyInfo != null)
                                lExpression = Expression.Property(parameterExpression, propertyInfo);
                            else
                                lExpression = Expression.Property(parameterExpression, item.LColumnName);
                        }
                        //右值表达式
                        Expression rEpression;// = GetConstantExpression(rData, columnType);
                        if (string.IsNullOrWhiteSpace(item.RColumnName))
                        {
                            Type rType = GetModelColumnType<T>(item.LColumnName);
                            object rValue = ChangeType(item.RValue, rType);
                            rEpression = GetConstantExpression(rValue);
                        }
                        else
                        {
                            PropertyInfo propertyInfo = tType.GetProperty(item.RColumnName);
                            if (propertyInfo != null)
                                rEpression = Expression.Property(parameterExpression, propertyInfo);
                            else
                                rEpression = Expression.Property(parameterExpression, item.RColumnName);
                        }
                        //获得子节点数据
                        Expression child = GetChildrenExpression(parameterExpression, lExpression, rEpression, item.ConditionsType);
                        //第一个节点既本体
                        if (predicateBodyChild == null)
                        {
                            predicateBodyChild = child;
                        }
                        else
                        {
                            if (child != null)
                            {
                                //根据逻辑判断类型拼接
                                switch (item.LogicalType)
                                {
                                    case Logical.And:
                                        predicateBodyChild = Expression.And(predicateBodyChild, child);
                                        break;
                                    case Logical.Or:
                                        predicateBodyChild = Expression.Or(predicateBodyChild, child);
                                        break;
                                    case Logical.Pass:
                                        break;
                                    default:
                                        break;
                                }
                            }
                        }
                    }
                    if (predicateBody == null)
                    {
                        predicateBody = predicateBodyChild;
                    }
                    else
                    {
                        if (predicateBody != null)
                        {
                            //根据逻辑判断类型拼接
                            switch (queryWhere.LogicalType)
                            {
                                case Logical.And:
                                    predicateBody = Expression.And(predicateBody, predicateBodyChild);
                                    break;
                                case Logical.Or:
                                    predicateBody = Expression.Or(predicateBody, predicateBodyChild);
                                    break;
                                case Logical.Pass:
                                    break;
                                default:
                                    break;
                            }
                        }
                    }
                }
                if (predicateBody != null)
                    expression = Expression.Lambda<Func<T, bool>>(predicateBody, new[] { parameterExpression });
            }
            return expression;
        }
        /// <summary>
        /// 获得表达式树
        /// </summary>
        /// <param name="parameterExpression">表达式成员</param>
        /// <param name="lExpression">左值表达式</param>
        /// <param name="rExpression">右值表达式</param>
        /// <param name="conditionsType">条件类型</param>
        /// <returns></returns>
        private Expression GetChildrenExpression(ParameterExpression parameterExpression, Expression lExpression, Expression rExpression, Conditions conditionsType)
        {
            if (conditionsType == Conditions.Pass || lExpression == null || rExpression == null)
            {
                return null;
            }
            Type columnType = lExpression.Type;

            Expression predicateBody = null;
            MethodInfo method;
            //先通过特殊的表达式获取方法，例如string的判断和数值的不同，需要特殊处理
            predicateBody = GetSpecialExpression(lExpression, rExpression, conditionsType);
            if (predicateBody == null)
            {
                //左侧的表达式节点
                switch (conditionsType)
                {
                    case Conditions.Equal://等于
                        predicateBody = Expression.Equal(lExpression, rExpression);
                        break;
                    case Conditions.NotEqual://不等于
                        predicateBody = Expression.NotEqual(lExpression, rExpression);
                        break;
                    case Conditions.LessThan://小于
                        predicateBody = Expression.LessThan(lExpression, rExpression);
                        break;
                    case Conditions.LessThanOrEqual://小于等于
                        predicateBody = Expression.LessThanOrEqual(lExpression, rExpression);
                        break;
                    case Conditions.GreaterThan://大于
                        predicateBody = Expression.GreaterThan(lExpression, rExpression);
                        break;
                    case Conditions.GreaterThanOrEqual://大于等于
                        predicateBody = Expression.GreaterThanOrEqual(lExpression, rExpression);
                        break;
                    case Conditions.Contains://字符串，包含
                        method = columnType.GetMethod("Contains", new[] { columnType });
                        predicateBody = Expression.Call(lExpression, method, rExpression);
                        break;
                    case Conditions.StartsWith://字符串，包含
                        method = columnType.GetMethod("StartsWith", new[] { columnType });
                        predicateBody = Expression.Call(lExpression, method, rExpression);
                        break;
                    case Conditions.EndsWith://字符串，包含
                        method = columnType.GetMethod("EndsWith", new[] { columnType });
                        predicateBody = Expression.Call(lExpression, method, rExpression);
                        break;
                    case Conditions.NotContains://字符串，不包含
                        method = columnType.GetMethod("Contains", new[] { columnType });
                        predicateBody = Expression.Not(Expression.Call(lExpression, method, rExpression));
                        break;
                    case Conditions.NotStartsWith://字符串，不包含
                        method = columnType.GetMethod("StartsWith", new[] { columnType });
                        predicateBody = Expression.Not(Expression.Call(lExpression, method, rExpression));
                        break;
                    case Conditions.NotEndsWith://字符串，不包含
                        method = columnType.GetMethod("EndsWith", new[] { columnType });
                        predicateBody = Expression.Not(Expression.Call(lExpression, method, rExpression));
                        break;
                    case Conditions.ListContains:
                        method = rExpression.Type.GetMethod("Contains", new[] { columnType });
                        predicateBody = Expression.Call(rExpression, method, lExpression);
                        break;
                    case Conditions.ListNotContains:
                        method = rExpression.Type.GetMethod("Contains", new[] { columnType });
                        predicateBody = Expression.Not(Expression.Call(rExpression, method, lExpression));
                        break;
                    default:
                        predicateBody = null;
                        break;
                }
            }
            return predicateBody;
        }
        /// <summary>
        /// 生成针对字符串对比的方法的表达式树
        /// </summary>
        /// <param name="lExpression">左值表达式</param>
        /// <param name="rExpression">右值表达式</param>
        /// <param name="conditionsType">比较类型</param>
        /// <returns></returns>
        virtual public Expression GetSpecialExpression(Expression lExpression, Expression rExpression, Conditions conditionsType)
        {
            Expression predicateBody = null;
            if (lExpression.Type == typeof(String))
            {
                MethodInfo method = lExpression.Type.GetMethod("CompareOrdinal", new Type[] { lExpression.Type, lExpression.Type });
                //比对结果
                Expression resuleExpression = Expression.Constant(0, typeof(int));
                predicateBody = Expression.Call(method, lExpression, rExpression);
                switch (conditionsType)
                {
                    case Conditions.LessThan://小于
                        predicateBody = Expression.LessThan(predicateBody, resuleExpression);
                        break;
                    case Conditions.LessThanOrEqual://小于等于
                        predicateBody = Expression.LessThanOrEqual(predicateBody, resuleExpression);
                        break;
                    case Conditions.GreaterThan://大于
                        predicateBody = Expression.GreaterThan(predicateBody, resuleExpression);
                        break;
                    case Conditions.GreaterThanOrEqual://大于等于
                        predicateBody = Expression.GreaterThanOrEqual(predicateBody, resuleExpression);
                        break;
                    default:
                        return null;
                }
            }
            return predicateBody;
        }
        /// <summary>
        /// 对值进行包装
        /// </summary>
        /// <param name="data">目标值</param>
        /// <returns></returns>
        virtual public Expression GetConstantExpression(object data)
        {
            ConstantExpression constantExpression = Expression.Constant(data, data.GetType());
            return constantExpression;
        }
    }
}
